home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume3 / dial < prev    next >
Encoding:
Internet Message Format  |  1986-11-30  |  31.3 KB

  1. From: "Oliver Laumann" <talcott!seismo!unido!tub!net>
  2. Subject: dial -- a state transition controlled communications program
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 118
  7. Submitted by: "Oliver Laumann" <talcott!seismo!unido!tub!net>
  8.  
  9. #! /bin/sh
  10. # This is a shell archive, meaning:
  11. # 1. Remove everything above the #! /bin/sh line.
  12. # 2. Save the resulting text in a file.
  13. # 3. Execute the file with /bin/sh (not csh) to create the files:
  14. #    README
  15. #    dial.1
  16. #    dial.c
  17. # This archive created: Thu Feb  6 15:15:02 1986
  18. export PATH; PATH=/bin:$PATH
  19. echo shar: extracting "'README'" '(1552 characters)'
  20. if test -f 'README'
  21. then
  22.     echo shar: will not over-write existing file "'README'"
  23. else
  24. cat << \SHAR_EOF > 'README'
  25. Dial is a (4.xBSD-only) communications program for tty lines.  It is
  26. similar to tip(1), though it doesn't support as many options as tip and
  27. no file transfer.  In addition to interactive access to a remote
  28. computer, dial can operate in a non-interactive way using a
  29. user-supplied state transition script.  This is useful for accessing
  30. certain `server logins' on remote machines without human interaction
  31. and in cases where the connect/login procedure for a remote computer is
  32. complex, e.g. when a remote system is accessed through a PABX, LAN, or
  33. terminal selector.
  34.  
  35. Compile dial using the following command:
  36.     cc -o dial dial.c -ltermcap
  37. (the termcap library is solely used to read entries from /etc/remote).
  38.  
  39. `dial.1' contains the manual page for dial.
  40.  
  41. `example' contains a sample state transition script that can be
  42. interpreted by dial.  It is probably of no particular use for you; we
  43. are using it here to connect to a server login on a remote UNIX machine
  44. through our local PABX (see the comments in `example').  But you can
  45. get an idea how a dial script looks like.
  46.  
  47. Dial makes use of select(2), remote(5), and the Berkeley-UNIX tty
  48. driver.  Thus, it is probably impossible to port it to non-BSD
  49. systems.  I don't know if it will run under 4.3BSD (we haven't
  50. got 4.3 here, yet).
  51.  
  52. Please mail bug-reports and useful modifications to:
  53.  
  54.    ...ihnp4!seismo!unido!tub!net   or   net@DB0TUI6.BITNET
  55.       ...!mcvax!unido!tub!net
  56.  
  57. Regards,
  58.     Oliver Laumann
  59.     Technical University of Berlin,
  60.     Communications and Operating Systems Research Group.
  61.  
  62. SHAR_EOF
  63. if test 1552 -ne "`wc -c < 'README'`"
  64. then
  65.     echo shar: error transmitting "'README'" '(should have been 1552 characters)'
  66. fi
  67. fi # end of overwriting check
  68. echo shar: extracting "'dial.1'" '(7835 characters)'
  69. if test -f 'dial.1'
  70. then
  71.     echo shar: will not over-write existing file "'dial.1'"
  72. else
  73. cat << \SHAR_EOF > 'dial.1'
  74. .TH DIAL 1 "23 January 1986"
  75. .UC 4
  76. .SH NAME
  77. dial \- connect to a remote system using a state transition script
  78. .SH SYNOPSIS
  79. .B dial
  80. [
  81. .B \-v
  82. ] [
  83. .B \-p
  84. ] [ \-l\f2line\fP
  85. ] [script-file [ arguments... ]]
  86. .SH DESCRIPTION
  87. .I Dial
  88. establishes a connection to a remote machine using
  89. a user-supplied state transition script.
  90. .I Dial
  91. is mainly used to communicate with remote server-logins in a
  92. non-interactive way, or for interactive sessions on a remote machine
  93. when the login/logout procedures should be carried out automatically.
  94. .PP
  95. The
  96. .B \-l\f2line\fP
  97. parameter specifies the line to be opened to establish the connection.
  98. If it is a relative pathname,
  99. .I dial
  100. prepends the string
  101. .I ``/dev/''
  102. to the specified line.
  103. If the line does not exist,
  104. .I dial
  105. interprets
  106. .I line
  107. as the system name of the remote machine and uses the file /etc/remote
  108. to find out how to reach the system and how the line parameters
  109. (e.g. baud rate) should be set; refer to
  110. .IR remote (5)
  111. for a full description.
  112. .PP
  113. The
  114. .B \-v
  115. option causes
  116. .I dial
  117. to print on standard output diagnostic information for each
  118. state transition.
  119. This can be helpful for debugging
  120. .I dial
  121. scripts.
  122. .PP
  123. .I Script-file
  124. contains the state transition table; if
  125. .I script-file
  126. is `\-', standard input is used.
  127. If
  128. .I script-file
  129. cannot be opened, the value of the environment variable ``DIALDIR''
  130. (if present) is prepended to the file name.  
  131. If this also fails, the path ``/usr/local/lib/dial/'' is prepended to
  132. the specified file name.
  133. .PP
  134. Lines in the script beginning with `#' are treated as comment lines.
  135. Comment lines and blank lines are ignored.
  136. All occurrences of `$\f2n\fP' in the script are substituted by the \f2n\fPth 
  137. optional argument
  138. from the command line; the first argument is `$0'.
  139. `$*' is substituted by the concatenation of all arguments separated
  140. by blanks.
  141. All occurrences of `${\f2symbol\fP}' are replaced by the value of
  142. the environment variable
  143. .I symbol.
  144. If
  145. .I symbol
  146. begins with `~' or `/', it is interpreted as the name of a file,
  147. and the contents of the file is interpolated into the
  148. script (\f2dial\fP performs
  149. .IR csh (1)-style
  150. expansion of `~' and `~user').
  151. .PP
  152. The script file contains a list of state definitions; each definition
  153. is of the form
  154. .RS
  155. .HP
  156. .nf
  157. state-name  send-string  [[time-out][,delay]]
  158. next-state  pattern  [output-string]
  159. next-state  pattern  [output-string]
  160. \0\0\0...
  161. .fi
  162. .RE
  163. .PP
  164. The first line of each state definition starts in column 0; the following
  165. lines are indented by spaces or tabs.
  166. .I
  167. State-name
  168. is the name of the state;
  169. .I send-string
  170. is a string that is sent to the remote system whenever the state is entered.
  171. When a state has been entered,
  172. .I dial
  173. receives data from the remote system until the characters read
  174. so far match one of the
  175. .I patterns
  176. listed in the state definitions.
  177. If a match occurs, the optional
  178. .I output-string
  179. is sent to the standard output, and then the corresponding
  180. .I next-state
  181. is entered.
  182. The order of the
  183. .I "next-state\-pattern"
  184. pairs is significant.
  185. .PP
  186. When the
  187. .B \-p
  188. options is given, the
  189. .I output-string
  190. field of each rule is ignored.
  191. Instead, all characters received from the remote system are sent
  192. to the standard output.
  193. .PP
  194. When a time-out occurs while receiving from the remote system,
  195. a transition to the state
  196. .B exit
  197. is performed (if not specified otherwise).
  198. The default time-out for each state is 10 seconds; it can be changed
  199. as described below.
  200. The optional
  201. .I time-out
  202. field in each state definition can be used to assign a different
  203. time-out value to this state.
  204. Likewise, the
  205. .I .delay
  206. field can be used to specify a delay (measured in seconds) to be
  207. performed before the
  208. .I send-string
  209. is transmitted.
  210. Both fields are optional.
  211. Thus,
  212. .B ``,1.5''
  213. means ``use the default time-out and a delay of 1.5 seconds''.
  214. .PP
  215. Each time the symbol `\\<' or `\\?' is encountered in the
  216. .I send-string
  217. field of a state definition,
  218. .I dial
  219. reads one input line from standard input and replaces the symbol
  220. with that line (not including the terminating newline).
  221. In case of `\\?', the read is performed in CBREAK mode with all
  222. special characters as well as echo turned off.
  223. This is useful when passwords should be interpolated into the
  224. data sent to the remote machine, but should not appear directly
  225. in the script.
  226. .PP
  227. .I Pattern
  228. is a simple character string; `.' within a pattern matches any character.
  229. The special pattern `*' matches anything (useful for catch-all rules);
  230. the pattern `#' denotes
  231. .IR timeout ,
  232. that is, the corresponding
  233. .I next-state
  234. is entered when a time-out occurs.
  235. In
  236. .IR output-string,
  237. the symbol `&'
  238. denotes the string that matched the corresponding pattern.
  239. This mechanism is used to print on standard output characters
  240. received from the remote system or diagnostic messages.
  241. The special interpretation of `.', `#', `*', `&', `$', `\\<', and `\\?' is
  242. suppressed when the symbol is preceded by `\\' (backslash).
  243. .PP
  244. .IR Send-string ,
  245. .IR pattern ,
  246. and
  247. .I output-string
  248. can be surrounded by double-quotes.
  249. This is useful when blanks should be included or to denote an
  250. empty string.
  251. The symbol `\\r' can be used to denote
  252. .IR "carriage return" ,
  253. `\\n' stands for
  254. .IR "line feed" .
  255. In addition, a `\\' followed by one to three octal digits stands for the
  256. character whose ASCII code is given by those digits.
  257. .PP
  258. When
  259. .I dial
  260. is called, a transition to the first state listed in the script file
  261. is performed.
  262. A transition to the special state
  263. .B exit
  264. causes
  265. .I dial
  266. to drop the connection and terminate.
  267. A transition to the state
  268. .B user
  269. causes
  270. .I dial
  271. to enter interactive mode.
  272. In this mode, all typed characters are transmitted to the remote machine,
  273. while all characters received from the remote machine are sent to
  274. standard output (that is,
  275. .I dial
  276. acts as if you had been connected to the remote machine using
  277. .IR tip (1)).
  278. .PP
  279. If no script file is given, or if the specified script-file does not
  280. contain any state definitions,
  281. .I dial
  282. performs a transition to the state
  283. .BR user ,
  284. that is, opens an interactive session.
  285. .PP
  286. If the special
  287. .I "escape character"
  288. (usually `~') followed by carriage return or space
  289. is typed in interactive mode,
  290. .I dial
  291. sends the
  292. .I "prompt string"
  293. (usually `:')
  294. to standard output and reads from standard input the name of a new state.
  295. If a valid state name has been entered,
  296. .I dial
  297. leaves the interactive mode and enters the given state.
  298. .PP
  299. If the
  300. .I "escape character"
  301. is followed by either `.' or the end-of-file character (usually `^D'),
  302. .I dial
  303. enters the state
  304. .B exit
  305. immediately, that is, closes the connection and terminates.
  306. .I Dial
  307. can be suspended by typing
  308. .I escape
  309. followed by the suspend process character (usually `^Z').
  310. .I Escape
  311. followed by `!' forks a shell;
  312. .I escape
  313. followed by `?' prints a summary of available
  314. .I escape
  315. commands.
  316. .PP
  317. Lines in the script file of the form
  318. .IP
  319. keyword = value
  320. .PP
  321. are used to assign values to certain parameters.
  322. Valid keywords are
  323. .B timeout
  324. (the default time-out to be used),
  325. .B delay
  326. (the default delay),
  327. .B line
  328. (the line to be opened),
  329. .B escape
  330. (the
  331. .IR "escape character" ),
  332. and
  333. .B prompt
  334. (the
  335. .IR "prompt string" ).
  336. The value for
  337. .B line
  338. is overridden by the
  339. .B \-l\f2line\fP
  340. option.
  341. .SH EXAMPLES
  342. The following simple script prints on standard output each character
  343. received from /dev/tty0.
  344. It terminates when a `.' is received or when a timeout of 0.5 second
  345. occurs.
  346. .PP
  347. .RS
  348. .nf
  349. # Simple example for a dial script:
  350. line=tty0
  351. loop "" .5
  352.     exit \\. "Done.\\n"
  353.     exit #  "Got time-out.\\n"
  354.     loop *  &
  355. .fi
  356. .RE
  357. .ta \w'/usr/spool/uucp/LCK..*\0\0\0'u
  358. .SH FILES
  359. .nf
  360. /usr/local    system-wide \f2dial\fP scripts
  361. .br
  362. ${DIALDIR}    private \f2dial\fP scripts
  363. .br
  364. /usr/spool/uucp/LCK..*    lock file for \f2uucp\fP and \f2tip\fP
  365. .fi
  366. .SH "SEE ALSO"
  367. tip(1C)
  368. .SH AUTHOR
  369. Oliver Laumann
  370. .SH BUGS
  371. The number of characters actually matched by the pattern `*'
  372. is not deterministic.
  373. SHAR_EOF
  374. if test 7835 -ne "`wc -c < 'dial.1'`"
  375. then
  376.     echo shar: error transmitting "'dial.1'" '(should have been 7835 characters)'
  377. fi
  378. fi # end of overwriting check
  379. echo shar: extracting "'dial.c'" '(21088 characters)'
  380. if test -f 'dial.c'
  381. then
  382.     echo shar: will not over-write existing file "'dial.c'"
  383. else
  384. cat << \SHAR_EOF > 'dial.c'
  385. /* dial -- dial a remote computer using a state transition table
  386.  *
  387.  * Copyright (c) 1986, Oliver Laumann, Technical University of Berlin.
  388.  * Not derived from licensed software.
  389.  *
  390.  * Permission is granted to freely use, copy, modify, and redistribute
  391.  * this software, provided that no attempt is made to gain profit from it,
  392.  * the author is not construed to be liable for any results of using the
  393.  * software, alterations are clearly marked as such, and this notice is
  394.  * not modified.
  395.  */
  396.  
  397. #include <stdio.h>
  398. #include <pwd.h>
  399. #include <sys/ioctl.h>
  400. #include <sys/time.h>
  401. #include <sys/file.h>
  402. #include <sys/errno.h>
  403. #include <sys/signal.h>
  404. #include <sys/types.h>
  405. #include <sys/stat.h>
  406.  
  407. #define TIMEOUT     10
  408. #define DELAY       0
  409. #define BUFLEN      1024
  410. #define EXIT        ((struct state *)0)
  411. #define LOCKFN      "/usr/spool/uucp/LCK..%s"
  412. #define DIALLIB     "/usr/local/lib/dial"
  413. #define REMOTE      "/etc/remote"
  414.  
  415. #define FULL        0      /* match() return values */
  416. #define PART        1
  417. #define NONE        2
  418.  
  419. #define ISSPACE(x) (x == ' ' || x == '\t')
  420. #define ISOCT(x)   (x >= '0' && x <= '7')
  421. #define ISDEC(x)   (x >= '0' && x <= '9')
  422.  
  423. char *script;
  424. char *line;
  425. int escape = '~';
  426. char *prompt = "\r\n:";
  427. struct timeval timeout = {TIMEOUT, 0};
  428. struct timeval delay = {DELAY, 0};
  429. char obuf[BUFLEN];
  430. char *obp = obuf;
  431. char rbuf[BUFLEN];
  432. char *rbp = rbuf;
  433. int vflag;
  434. int pflag;
  435. int ttyf;
  436. struct sgttyb ottyb;
  437. struct sgttyb consb;
  438. struct tchars otc;
  439. struct ltchars oltc;
  440. short coflags;
  441. int ttydone;
  442. int consdone;
  443. char *myname;
  444. char **argp;
  445. int numargs;
  446. char lockfile[BUFLEN];
  447. int locked;
  448. int undef;
  449. char *dialdir;
  450. int baud = -1;
  451. int bdconst[] = {
  452.     B50, B75, B110, B134, B150, B200, B300, B600,
  453.     B1200, B1800, B2400, B4800, B9600, EXTA, EXTB
  454. };
  455. int bdnum[] = {
  456.     50, 75, 110, 134, 150, 200, 300, 600,
  457.     1200, 1800, 2400, 4800, 9600, 19200, 38400, 0
  458. };
  459.  
  460. #define S_STATE  1
  461.  
  462. struct state {
  463.     struct state *s_next;
  464.     char *s_string;
  465.     int s_len;
  466.     char *s_name;
  467.     char *s_output;
  468.     int s_outlen;
  469.     struct timeval s_timeout;
  470.     struct timeval s_delay;
  471.     int s_stat;
  472. } *stab;
  473.  
  474. extern errno;
  475. extern char **environ;
  476. char *malloc(), *alloc(), *memsav(), *getenv(), *tgetstr(), *fnexpand();
  477. struct state *enter(), *receive(), *next_state(), *user();
  478. struct passwd *getpwnam(), *getpwuid();
  479. long atol();
  480.  
  481. main (ac, av) char **av; {
  482.     register struct state *next;
  483.     register char *p;
  484.     int Exit();
  485.  
  486.     myname = ac < 1 ? "dial" : av[0];
  487.     if (ac < 1) {
  488. help:   err ("Use: %s [-v] [-p] [-lline] [script-file [args...]]\n", myname);
  489.     }
  490.     while (--ac) {
  491.     p = *++av;
  492.     if (*p == '-' && p[1] != '\0') {
  493.         if (*++p == 'v') {
  494.         ++vflag;
  495.         } else if (*p == 'p') {
  496.         ++pflag;
  497.         } else if (*p == 'l') {
  498.         line = ++p;
  499.         } else goto help;
  500.     } else if (!script) {
  501.         script = p;
  502.     } else {
  503.         argp = av;
  504.         numargs = ac;
  505.         break;
  506.     }
  507.     }
  508.     if (script) {
  509.     dialdir = getenv ("DIALDIR");
  510.     get_script ();
  511.     check_script ();
  512.     }
  513.     if (!line || line[0] == '\0')
  514.     err ("No tty line specified.\n");
  515.     signal (SIGHUP, Exit);
  516.     signal (SIGINT, Exit);
  517.     open_line ();
  518.     if (!stab)
  519.     say ("Connected.\n");
  520.     set_tty ();
  521.     next = (stab ? stab : user ());
  522.     while (next != EXIT)
  523.     next = enter (next);
  524.     if (vflag)
  525.     say ("State `exit' -- done.\n");
  526.     Exit (0);
  527. }
  528.  
  529. get_script () {
  530.     FILE *f = NULL;
  531.     char fn[BUFLEN], lbuf[BUFLEN], buf[BUFLEN];
  532.     register char *p, *s;
  533.     register struct state *sp, *tmp;
  534.     register c, delim, lno = 0;
  535.     struct stat st;
  536.  
  537.     if (!strcmp (script, "-")) {
  538.     f = stdin;
  539.     script = "stdin";
  540.     } else {
  541.     if (stat (script, &st) || (st.st_mode & S_IFMT) == S_IFDIR
  542.                    || (f = fopen (script, "r")) == NULL) {
  543.         if (dialdir) {
  544.         sprintf (fn, "%s/%s", dialdir, script);
  545.         f = fopen (fn, "r");
  546.         }
  547.         if (f == NULL) {
  548.         sprintf (fn, "%s/%s", DIALLIB, script);
  549.         if ((f = fopen (fn, "r")) == NULL)
  550.             err ("Cannot open `%s'.\n", script);
  551.         }
  552.     }
  553.     }
  554.     while (1) {
  555.     ++lno;
  556.     if (fgetstr (f, lbuf, BUFLEN-1) == EOF)
  557.         break;
  558.     if (!expand_args (lbuf, buf)) goto error;
  559.     for (p = buf; ISSPACE(*p); ++p) ;
  560.     if (*p == '#' || *p == '\0')
  561.         continue;
  562.     if (p == buf) {
  563.         if (getkey (p))
  564.         continue;
  565.     } else if (!stab) goto error;
  566.     tmp = (struct state *)alloc (sizeof (struct state));
  567.     tmp->s_stat = (p == buf ? S_STATE : 0);
  568.     for (s = p; *s && !ISSPACE(*s); ++s) ;
  569.     if (*s == '\0') goto error;
  570.     *s = '\0';
  571.     tmp->s_name = memsav (p, s-p+1);
  572.     for (++s; ISSPACE(*s); ++s) ;
  573.     if (delim = (*s == '"')) ++s;
  574.     for (p = s; *s && (delim ? *s != '"' : !ISSPACE(*s)); ++s) ;
  575.     if (delim) {
  576.         if (*s == '\0') goto error;
  577.         *s++ = '\0';
  578.     }
  579.     c = *s;
  580.     *s = '\0';
  581.     tmp->s_len = cook (p, p);
  582.     tmp->s_string = memsav (p, tmp->s_len);
  583.     tmp->s_outlen = 0;
  584.     tmp->s_timeout = timeout;
  585.     tmp->s_delay = delay;
  586.     if (c) {
  587.         for (++s; ISSPACE(*s); ++s) ;
  588.         if (*s) {
  589.         if (delim = (*s == '"')) ++s;
  590.         for (p = s; *s && (delim ? *s != '"' : !ISSPACE(*s)); ++s) ;
  591.         if (delim) {
  592.             if (*s == '\0') goto error;
  593.             *s++ = '\0';
  594.         }
  595.         *s = '\0';
  596.         if (tmp->s_stat & S_STATE) {
  597.             if (!set_par (tmp, p)) goto error;
  598.         } else {
  599.             tmp->s_outlen = cook (p, p);
  600.             tmp->s_output = memsav (p, tmp->s_outlen);
  601.         }
  602.         }
  603.     }
  604.     if (stab)
  605.         sp = sp->s_next = tmp;
  606.     else
  607.         stab = sp = tmp;
  608.     sp->s_next = 0;
  609.     }
  610.     fclose (f);
  611.     return;
  612. error:
  613.     err ("%s: syntax error in line %d.\n", script, lno);
  614. }
  615.  
  616. expand_args (from, to) char *from, *to; {
  617.     register char *p, *q, *t, *s = to, *endp = to+BUFLEN-1;
  618.     register n;
  619.     FILE *f;
  620.  
  621.     for (p = from; *p; ++p) {
  622.     if (s == endp)
  623.         break;
  624.     if (*p == '$') {
  625.         if (p > from && p[-1] == '\\') {
  626.         s[-1] = '$';    
  627.         } else if (ISDEC(p[1])) {
  628.         if (*++p - '0' <= numargs) {
  629.             for (q = argp[*p - '0']; s < endp && *q; *s++ = *q++)
  630.             ;
  631.         }
  632.         } else if (p[1] == '*') {
  633.         for (n = 0; n < numargs; ++n) {
  634.             for (q = argp[n]; s < endp && *q; *s++ = *q++)
  635.             ;
  636.             if (n < numargs-1 && s < endp)
  637.             *s++ = ' ';
  638.         }
  639.         ++p;
  640.         } else if (p[1] == '{') {
  641.         ++p;
  642.         for (q = ++p; *p && *p != '}'; ++p) ;
  643.         if (*p == '\0') return (0);
  644.         *p = '\0';
  645.         if (*q == '~' || *q == '/') {
  646.             t = fnexpand (q);
  647.             if ((f = fopen (t, "r")) == NULL)
  648.             err ("Cannot open `%s'.\n", t);
  649.             while ((n = getc (f)) != EOF && s < endp)
  650.             *s++ = n;
  651.             fclose (f);
  652.         } else if (t = getenv (q)) {
  653.             while (s < endp && *t)
  654.             *s++ = *t++;
  655.         }
  656.         } else *s++ = '$';
  657.     } else *s++ = *p;
  658.     }
  659.     *s = '\0';
  660.     return (1);
  661. }
  662.  
  663. char *fnexpand (s) char *s; {
  664.     static char buf[BUFLEN];
  665.     struct passwd *pw;
  666.     register char c, *p, *q = s+1;
  667.  
  668.     if (*s != '~')
  669.     return (s);
  670.     for (p = q; (c = *p) && c != '/'; ++p) ;
  671.     *p = '\0';
  672.     if (p == q) {
  673.     if (!(pw = getpwuid (getuid ())))
  674.         err ("Cannot get home directory.\n");
  675.     } else if (!(pw = getpwnam (q)))
  676.     err ("Unknown user: %s.\n", q);
  677.     if (c) {
  678.     sprintf (buf, "%s/%s", pw->pw_dir, ++p);
  679.     return (buf);
  680.     } else return (pw->pw_dir);
  681. }
  682.  
  683. set_par (sp, s) struct state *sp; char *s; {
  684.     register char *p;
  685.  
  686.     for (p = s; *p && *p != ','; ++p) ;
  687.     if (*p == ',')
  688.     *p++ = '\0';
  689.     if (*s) {
  690.     if (!isnum (s)) return (0);
  691.     set_time (&sp->s_timeout, s);
  692.     }
  693.     if (*p) {
  694.     if (!isnum (p)) return (0);
  695.     set_time (&sp->s_delay, p);
  696.     }
  697.     return (1);
  698. }
  699.  
  700. set_time (tp, s) struct timeval *tp; char *s; {
  701.     register char *p;
  702.     register long div;
  703.  
  704.     for (p = s; *p && *p != '.'; ++p) ;
  705.     if (*p == '.')
  706.     *p++ = '\0';
  707.     tp->tv_sec = atoi (s);
  708.     if (p) {
  709.     p[6] = '\0';
  710.     tp->tv_usec = atol (p);
  711.     div = 1;
  712.     while (*p) {
  713.         div *= 10;
  714.         ++p;
  715.     }
  716.     tp->tv_usec *= 1000000L / div;
  717.     } else tp->tv_usec = 0;
  718. }
  719.  
  720. getkey (s) char *s; {
  721.     register char *k, *p, *q;
  722.  
  723.     for (p = s; *p; ++p)
  724.     if (*p == '=' && !(p > s && p[-1] == '\\'))
  725.         break;
  726.     if (*p == '\0') return (0);
  727.     for (*p++ = '\0'; ISSPACE(*p); ++p) ;
  728.     for (q = p; *q && !ISSPACE(*q); ++q) ;
  729.     *q = '\0';
  730.     for (k = s; *k && !ISSPACE(*k); ++k) ;
  731.     *k = '\0';
  732.     if (!strcmp (s, "line")) {
  733.     if (*p == '\0')
  734.         err ("Bad value for keyword `line'.\n");
  735.     if (!line)
  736.         line = memsav (p, strlen (p)+1);
  737.     } else if (!strcmp (s, "timeout")) {
  738.     if (!isnum (p))
  739.         err ("Bad value for keyword `timeout'.\n");
  740.     set_time (&timeout, p);
  741.     } else if (!strcmp (s, "delay")) {
  742.     if (!isnum (p))
  743.         err ("Bad value for keyword `delay'.\n");
  744.     set_time (&delay, p);
  745.     } else if (!strcmp (s, "escape")) {
  746.     if (strlen (p) != 1)
  747.         err ("Bad value for keyword `escape'.\n");
  748.     escape = *p & 0177;
  749.     } else if (!strcmp (s, "prompt")) {
  750.     prompt = memsav (p, strlen (p)+1);
  751.     } else err ("Invalid keyword `%s'.\n", s);
  752.     return (1);
  753. }
  754.  
  755. check_script () {
  756.     register struct state *sp;
  757.  
  758.     for (sp = stab; sp; sp = sp->s_next) {
  759.     if (!(sp->s_stat & S_STATE)) continue;
  760.     if (!sp->s_next || (sp->s_next->s_stat & S_STATE))
  761.         err ("No transitions for state `%s'.\n", sp->s_name);
  762.     }
  763. }
  764.  
  765. struct state *enter (sp) struct state *sp; {
  766.     if (vflag)
  767.     say ("Entering state %s.\n", sp->s_name);
  768.     if (sp->s_delay.tv_sec || sp->s_delay.tv_usec) {
  769.     flushbuf ();
  770.     if (select (0, (int *)0, (int *)0, (int *)0, &sp->s_delay) == -1) {
  771.         perror ("select");
  772.         Exit (1);
  773.     }
  774.     }
  775.     sendstr (sp->s_string, sp->s_len);
  776.     return (receive (sp));
  777. }
  778.  
  779. sendstr (sbuf, len) char *sbuf; register len; {
  780.     char buf[BUFLEN], ibuf[BUFLEN];
  781.     register char *s, *p, *t;
  782.     register n;
  783.     char c;
  784.  
  785.     for (s = sbuf, p = buf; len && p < buf+BUFLEN; --len, ++p, ++s) {
  786.     if (*s == '\\') {
  787.         if (s[1] == '\\') {
  788.         *p = '\\'; ++s;
  789.         } else if (s[1] == '<' || s[1] == '?') {
  790.         ++s;
  791.         flushbuf ();
  792.         if (*s == '<') {
  793.             n = Read (0, ibuf, BUFLEN);
  794.             c = '\n';
  795.         } else {
  796.             set_cons ();
  797.             for (t = ibuf, n = 0; n < BUFLEN; ++n, ++t) {
  798.             Read (0, t, 1);
  799.             if (*t == '\r') break;
  800.             }
  801.             reset_cons ();
  802.             if (!pflag)
  803.             say ("\n");
  804.             c = '\r';
  805.         }
  806.         for (t = ibuf; n && *t != c && p < buf+BUFLEN; --n)
  807.             *p++ = *t++;
  808.         } else *p = '\\';
  809.     } else *p = *s;
  810.     }
  811.     if (vflag) {
  812.     say ("Sending \""); prstr (buf, p-buf); say ("\".\n");
  813.     }
  814.     if (p > buf)
  815.     Write (ttyf, buf, p-buf);
  816. }
  817.  
  818. fill (sp) struct state *sp; {
  819.     register i;
  820.     register char *p;
  821.     int rbits = (1 << ttyf);
  822.  
  823.     if (rbp == rbuf+BUFLEN)
  824.     err ("Pattern space overflow.\n");
  825.     flushbuf ();
  826.     i = select (ttyf+1, &rbits, (int *)0, (int *)0, &sp->s_timeout);
  827.     if (i == -1) {
  828.     perror ("select");
  829.     Exit (1);
  830.     }
  831.     if (i == 0) {
  832.     if (vflag)
  833.         say ("   Got time-out.\n");
  834.     return (0);
  835.     }
  836.     i = Read (ttyf, rbp, rbuf+BUFLEN-rbp);
  837.     p = rbp;
  838.     do {
  839.     *p++ &= 0177;
  840.     } while (--i);
  841.     if (vflag) {
  842.     say ("   Got \""); prstr (rbp, p-rbp); say ("\".\n");
  843.     }
  844.     if (pflag)
  845.     Write (1, rbp, p-rbp);
  846.     rbp = p;
  847.     return (1);
  848. }
  849.  
  850. shift (n) {
  851.     bcopy (rbuf+n, rbuf, rbp-rbuf-n);
  852.     rbp -=n;
  853. }
  854.  
  855. struct state *receive (sp) struct state *sp; {
  856.     register struct state *p, *fullp, *partp;
  857.     register r, mlen, dofill = 0;
  858.     int len;
  859.  
  860.     while (1) {
  861.     if ((rbp == rbuf || dofill) && !fill (sp)) {
  862.         rbp = rbuf;
  863.         for (p = sp->s_next; p && !(p->s_stat & S_STATE); p = p->s_next) {
  864.         if (p->s_len == 1 && p->s_string[0] == '#') {
  865.             output (p, (char *)0, 0);
  866.             return (next_state (p->s_name, 1));
  867.         }
  868.         }
  869.         return (EXIT);
  870.     }
  871.     dofill = 0;
  872.     fullp = partp = 0;
  873.     for (p = sp->s_next; p && !(p->s_stat & S_STATE); p = p->s_next) {
  874.         if (p->s_len == 1 && p->s_string[0] == '#')
  875.         continue;
  876.         r = match (rbuf, rbp-rbuf, p->s_string, p->s_len, &len);
  877.         if (r == FULL && !fullp) {
  878.         fullp = p;
  879.         mlen = len;
  880.         } else if (r == PART && !partp) {
  881.         partp = p;
  882.         }
  883.     }
  884.     if (!fullp && !partp) {
  885.         shift (1);
  886.         if (vflag)
  887.         say ("No match -- shift(1).\n");
  888.         continue;
  889.     }
  890.     if (fullp && !partp) {
  891.         if (fullp->s_len == 1 && fullp->s_string[0] == '*')
  892.         mlen = amatch (sp);
  893.         if (vflag)
  894.         say ("Full match <%s> -- shift(%d).\n", fullp->s_name, mlen);
  895.         output (fullp, rbuf, mlen);
  896.         shift (mlen);
  897.         return (next_state (fullp->s_name, 1));
  898.     }
  899.     if (vflag)
  900.         say ("Partial match <%s>.\n", partp->s_name);
  901.     dofill = 1;
  902.     }
  903. }
  904.  
  905. amatch (ap) struct state *ap; {
  906.     register struct state *sp;
  907.     register char *p;
  908.     int notused;
  909.  
  910.     for (p = rbuf; p < rbp; ++p) {
  911.     for (sp = ap->s_next; sp && !(sp->s_stat & S_STATE); sp = sp->s_next) {
  912.         if (sp->s_len == 1 &&
  913.             (sp->s_string[0] == '#' || sp->s_string[0] == '*'))
  914.         continue;
  915.         if (match (p, rbp-p, sp->s_string, sp->s_len, ¬used) != NONE)
  916.         goto done;
  917.     }
  918.     }
  919. done:
  920.     return (p-rbuf);
  921. }
  922.  
  923. match (s, slen, t, tlen, len) register char *s, *t; register slen, tlen;
  924.     int *len; {
  925.     register otlen = tlen, oslen = slen;
  926.  
  927.     if (tlen == 1 && t[0] == '*')
  928.     return (FULL);
  929.     while (tlen) {
  930.     if (!slen)
  931.         return (PART);
  932.     if (*t != '\\') {
  933.         if (*s != *t)
  934.         if (*t != '.' || (tlen < otlen && t[-1] == '\\'))
  935.             return (NONE);
  936.         ++s; --slen;
  937.     }
  938.     ++t; --tlen;
  939.     }
  940.     *len = oslen-slen;
  941.     return (FULL);
  942. }
  943.  
  944. struct state *next_state (s, noise) char *s; {
  945.     register struct state *sp;
  946.  
  947.     undef = 0;
  948.     if (!strcmp (s, "exit"))
  949.     return (EXIT);
  950.     if (!strcmp (s, "user")) {
  951.     return (user ());
  952.     }
  953.     for (sp = stab; sp; sp = sp->s_next)
  954.     if ((sp->s_stat & S_STATE) && !strcmp (sp->s_name, s))
  955.         return (sp);
  956.     ++undef;
  957.     if (noise)
  958.     err ("State `%s' not found in script.\n", s);
  959. #ifdef lint
  960.     return (sp);
  961. #endif
  962. }
  963.  
  964. struct state *user () {
  965.     char c, buf[BUFLEN];
  966.     register n, l, gotesc = 0;
  967.     int rbits;
  968.     register char *p, *s;
  969.     register struct state *sp;
  970.  
  971.     flushbuf ();
  972.     signal (SIGINT, SIG_IGN);
  973.     set_cons ();
  974.     while (1) {
  975.     rbits = (1 << 0) | (1 << ttyf);
  976.     n = select (ttyf+1, &rbits, (int *)0, (int *)0, (struct timeval *)0);
  977.     if (n == -1) {
  978.         perror ("select");
  979.         Exit (1);
  980.     }
  981.     if (rbits & (1 << 0)) {
  982.         if (gotesc) {
  983.         Read (0, &c, 1);
  984.         if (c == '.' || c == otc.t_eofc) {
  985.             reset_cons ();
  986.             say ("\nConnection closed.\n");
  987.             return (EXIT);
  988.         } else if (c == oltc.t_suspc) {
  989.             reset_cons ();
  990.             kill (getpid (), SIGTSTP);
  991.             set_cons ();
  992.         } else if (c == '!') {
  993.             reset_cons ();
  994.             say ("\n");
  995.             if (system ((s = getenv ("SHELL")) ? s : "/bin/sh") == 127)
  996.             say ("Cannot execute shell.\n");
  997.             else say ("\n!\n");
  998.             set_cons ();
  999.         } else if (c == '?') {
  1000.             reset_cons ();
  1001.             print_help ();
  1002.             set_cons ();
  1003.         } else if (c == ' ' || c == '\r') {
  1004.             Write (1, prompt, strlen (prompt));
  1005.             reset_cons ();
  1006.             if ((n = Read (0, buf, BUFLEN)) > 1) {
  1007.             buf[n-1] = '\0';
  1008.             for (p = buf; ISSPACE(*p); ++p) ;
  1009.             for (s = p; *s && !ISSPACE(*s); ++s) ;
  1010.             *s = '\0';
  1011.             if (s > p) {
  1012.                 sp = next_state (p, 0);
  1013.                 if (undef) {
  1014.                 say ("`"); prstr (p, s-p);
  1015.                 say ("' is undefined.\n");
  1016.                 } else {
  1017.                 signal (SIGINT, Exit);
  1018.                 return (sp);
  1019.                 }
  1020.             }
  1021.             }
  1022.             set_cons ();
  1023.         } else Write (ttyf, &c, 1);
  1024.         gotesc = 0;
  1025.         } else {
  1026.         n = Read (0, buf, BUFLEN);
  1027.         for (p = buf, l = n; l && *p != escape; ++p, --l) ;
  1028.         if (l) {
  1029.             if (p > buf)
  1030.             Write (ttyf, buf, p-buf);
  1031.             ++gotesc;
  1032.             while (--l)
  1033.             ioctl (0, TIOCSTI, ++p);
  1034.         } else Write (ttyf, buf, n);
  1035.         }
  1036.     }
  1037.     if (rbits & (1 << ttyf))
  1038.         Write (1, buf, Read (ttyf, buf, BUFLEN));
  1039.     }
  1040. }
  1041.  
  1042. print_help () {
  1043.     char e[3];
  1044.  
  1045.     if (escape >= ' ' && escape <= '~')
  1046.     sprintf (e, "%c", escape);
  1047.     else
  1048.     sprintf (e, "^%c", escape ^ '@');
  1049.     say ("\n%s.       close connection and terminate.\n", e);
  1050.     say ("%s!       fork a shell.\n", e);
  1051.     say ("%s^Z      suspend %s.\n", e, myname);
  1052.     say ("%s<CR>\n", e);
  1053.     say ("%s<SPACE> leave interactive mode and enter new state.\n", e);
  1054. }
  1055.  
  1056. output (sp, buf, len) struct state *sp; char *buf; {
  1057.     char tbuf[BUFLEN];
  1058.     register char *s = sp->s_output, *p = tbuf;
  1059.     register i, olen = sp->s_outlen;
  1060.  
  1061.     if (pflag || !olen)
  1062.     return;
  1063.     while (olen) {
  1064.     if (p == tbuf+BUFLEN) break;
  1065.     if (*s == '&' && !(s > sp->s_output && s[-1] == '\\')) {
  1066.         for (i = 0; i < len && p < tbuf+BUFLEN; ++i)
  1067.         *p++ = buf[i];
  1068.     } else if (*s != '\\') *p++ = *s;
  1069.     ++s;
  1070.     --olen;
  1071.     }
  1072.     if (vflag) {
  1073.     say ("Print \""); prstr (tbuf, p-tbuf); say ("\".\n");
  1074.     } else {
  1075.     outbuf (tbuf, p-tbuf);
  1076.     }
  1077. }
  1078.  
  1079. outbuf (buf, len) register char *buf; register len; {
  1080.     register l;
  1081.  
  1082. again:
  1083.     l = obuf + BUFLEN - obp;
  1084.     if (l > len) l = len;
  1085.     bcopy (buf, obp, l);
  1086.     obp += l;
  1087.     if (len -= l) {
  1088.     flushbuf ();
  1089.     buf += l;
  1090.     goto again;
  1091.     }
  1092. }
  1093.  
  1094. flushbuf () {
  1095.     if (obp > obuf) {
  1096.     Write (1, obuf, obp-obuf);
  1097.     obp = obuf;
  1098.     }
  1099. }
  1100.  
  1101. open_line () {
  1102.     char fn[BUFLEN];
  1103.     char tbuf[1024], cbuf[1024];
  1104.     int i, f;
  1105.     struct stat statbuf;
  1106.     char **oldenv, *envp[2], *p, *pp = cbuf;
  1107.  
  1108.     sprintf (fn, line[0] == '/' ? "%s" : "/dev/%s", line);
  1109.     if (stat (fn, &statbuf) == -1 && errno == ENOENT) {
  1110.     sprintf (tbuf, "TERMCAP=%s", REMOTE);
  1111.     envp[0] = memsav (tbuf, strlen (tbuf));
  1112.     envp[1] = 0;
  1113.     oldenv = environ;
  1114.     environ = envp;
  1115.     if ((i = tgetent (tbuf, line)) == -1)
  1116.         err ("Cannot open `%s'.\n", REMOTE);
  1117.     else if (i == 0)
  1118.         err ("`%s': no such line or remote system.\n", line);
  1119.     if ((p = tgetstr ("dv", &pp)) == 0)
  1120.         err ("No `dv' capability for system `%s'.\n", line);
  1121.     strcpy (fn, p);
  1122.     if ((baud = tgetnum ("br")) != -1) {
  1123.         for (i = 0; bdnum[i] && baud != bdnum[i]; ++i) ;
  1124.         if (!bdnum[i])
  1125.         err ("Invalid baud rate %d for system `%s'.\n", baud, line);
  1126.         baud = bdconst[i];
  1127.     }
  1128.     environ = oldenv;
  1129.     }
  1130.     if (!strncmp (fn, "/dev/", 5)) {
  1131.     sprintf (lockfile, LOCKFN, fn+5);
  1132.     if ((f = open (lockfile, O_CREAT|O_EXCL, 0)) == -1) {
  1133.         if (errno == EEXIST)
  1134.         err ("`%s' is already in use.\n", line);
  1135.         perror (lockfile);
  1136.         Exit (1);
  1137.     }
  1138.     locked++;
  1139.     close (f);
  1140.     }
  1141.     if ((ttyf = open (fn, O_RDWR)) == -1) {
  1142.     perror (line);
  1143.     Exit (1);
  1144.     }
  1145.     ioctl (ttyf, TIOCHPCL, (char *)0);   /* Hang up phone on last close. */
  1146. }
  1147.  
  1148. set_cons () {
  1149.     struct tchars tc;
  1150.     struct ltchars ltc;
  1151.  
  1152.     ioctl (0, TIOCGETP, &consb);
  1153.     coflags = consb.sg_flags;
  1154.     consb.sg_flags &= ~ECHO;
  1155.     consb.sg_flags &= ~CRMOD;
  1156.     consb.sg_flags |= CBREAK;
  1157.     ioctl (0, TIOCSETP, &consb);
  1158.     ioctl (0, TIOCGETC, &tc);
  1159.     otc = tc;
  1160.     tc.t_intrc = tc.t_quitc = -1;
  1161.     ioctl (0, TIOCSETC, &tc);
  1162.     ioctl (0, TIOCGLTC, <c);
  1163.     oltc = ltc;
  1164.     ltc.t_suspc = ltc.t_dsuspc = ltc.t_lnextc = -1;
  1165.     ioctl (0, TIOCSLTC, <c);
  1166.     ++consdone;
  1167. }
  1168.  
  1169. reset_cons () {
  1170.     if (consdone) {
  1171.     consb.sg_flags = coflags;
  1172.     ioctl (0, TIOCSETP, &consb);
  1173.     ioctl (0, TIOCSETC, &otc);
  1174.     ioctl (0, TIOCSLTC, &oltc);
  1175.     consdone = 0;
  1176.     }
  1177. }
  1178.  
  1179. set_tty () {
  1180.     struct sgttyb ttyb;
  1181.     ioctl (ttyf, TIOCGETP, &ttyb);
  1182.     ottyb = ttyb;
  1183.     ttyb.sg_flags &= ~ECHO;
  1184.     ttyb.sg_flags &= ~CRMOD;
  1185.     ttyb.sg_flags |= (RAW|TANDEM);
  1186.     if (baud != -1)
  1187.     ttyb.sg_ispeed = ttyb.sg_ospeed = baud;
  1188.     ioctl (ttyf, TIOCSETP, &ttyb);
  1189.     ++ttydone;
  1190. }
  1191.  
  1192. reset_tty () {
  1193.     if (ttydone)
  1194.     ioctl (ttyf, TIOCSETP, &ottyb);
  1195. }
  1196.  
  1197. Exit (c) {
  1198.     flushbuf ();
  1199.     reset_tty ();
  1200.     reset_cons ();
  1201.     if (locked) {
  1202.     if (unlink (lockfile) == -1)
  1203.         perror (lockfile);
  1204.     }
  1205.     exit (c);
  1206. }
  1207.  
  1208. Write (f, buf, len) char *buf; {
  1209.     if (write (f, buf, len) != len) {
  1210.     if (errno == EIO)
  1211.         err ("Lost line.\n");
  1212.     else {
  1213.         perror ("write");
  1214.         Exit (1);
  1215.     }
  1216.     }
  1217. }
  1218.  
  1219. Read (f, buf, len) char *buf; {
  1220.     register n;
  1221.     if ((n = read (f, buf, len)) == -1) {
  1222.     if (errno == EIO)
  1223.         err ("Lost line.\n");
  1224.     else {
  1225.         perror ("read");
  1226.         Exit (1);
  1227.     }
  1228.     }
  1229.     if (n == 0)
  1230.     err ("EOF on tty.\n");
  1231.     return (n);
  1232. }
  1233.  
  1234. char *memsav (s, len) char *s; {
  1235.     register char *p;
  1236.     p = alloc (len);
  1237.     bcopy (s, p, len);
  1238.     return (p);
  1239. }
  1240.  
  1241. char *alloc (n) {
  1242.     register char *p;
  1243.     if ((p = malloc (n)) == 0)
  1244.     err ("Out of memory.\n");
  1245.     return (p);
  1246. }
  1247.  
  1248. cook (dst, src) char *dst, *src; {
  1249.     register char *p, *t;
  1250.     register n, c;
  1251.  
  1252.     for (p = dst, t = src; c = *t; ++t) {
  1253.     if (c == '\\') {
  1254.         if (ISOCT(t[1])) {
  1255.         for (n = c = 0; n < 3; ++n) {
  1256.             c = (c << 3) | (*++t - '0');
  1257.             if (!ISOCT(t[1]))
  1258.             break;
  1259.         }
  1260.         } else {
  1261.         if (t[1] == 'r') {
  1262.             c = '\r'; ++t;
  1263.         } else if (t[1] == 'n') {
  1264.             c = '\n'; ++t;
  1265.         } else if (t[1] == '\0')
  1266.             break;
  1267.         }
  1268.     }
  1269.     *p = c; ++p;
  1270.     }
  1271.     return (p - dst);
  1272. }
  1273.  
  1274. prstr (s, len) char *s; int len; {
  1275.     register c, l;
  1276.  
  1277.     for (l = len; l; --l) {
  1278.     if ((c = *s++) == '\r')
  1279.         say ("\\r");
  1280.     else if (c == '\n')
  1281.         say ("\\n");
  1282.     else if (c < ' ' || c > '~')
  1283.         say ("\\%03o", c & 0377);
  1284.     else say ("%c", c);
  1285.     }
  1286. }
  1287.  
  1288. fgetstr (f, buf, len) register FILE *f; char *buf; {
  1289.     register c, n;
  1290.     register char *p = buf;
  1291.  
  1292.     n = len;
  1293.     while ((c = getc (f)) != EOF && c != '\n')
  1294.     if (n) {
  1295.         --n; *p++ = c;
  1296.     }
  1297.     *p = '\0';
  1298.     return (c == EOF && p == buf ? EOF : p - buf);
  1299. }
  1300.  
  1301. isnum (s) register char *s; {
  1302.     register point = 0;
  1303.  
  1304.     if (*s == '\0') return (0);
  1305.     for ( ; *s; ++s) {
  1306.     if (*s == '.') {
  1307.         if (point) return (0);
  1308.         ++point;
  1309.     } else if (!ISDEC(*s)) return (0);
  1310.     }
  1311.     return (1);
  1312. }
  1313.  
  1314. /*VARARGS1*/
  1315. say (fmt, p1, p2, p3, p4) char *fmt; {
  1316.     fprintf (stdout, fmt, p1, p2, p3, p4);
  1317. }
  1318.  
  1319. /*VARARGS1*/
  1320. err (fmt, p1, p2, p3, p4) char *fmt; {
  1321.     fprintf (stderr, fmt, p1, p2, p3, p4);
  1322.     Exit (1);
  1323. }
  1324. SHAR_EOF
  1325. if test 21088 -ne "`wc -c < 'dial.c'`"
  1326. then
  1327.     echo shar: error transmitting "'dial.c'" '(should have been 21088 characters)'
  1328. fi
  1329. fi # end of overwriting check
  1330. #    End of shell archive
  1331. exit 0
  1332.  
  1333.